/*
 * Copyright 2011 Daniel Krieg
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.code.csf.locators;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NameNotFoundException;
import javax.naming.NamingException;
import java.util.Hashtable;
import java.util.Properties;

public class JndiObjectLocator {
    public static final String CONTAINER_PREFIX = "java:comp/env/";
    private boolean resourceRef = false;
    private Properties environment;
    private String jndiName;
    private Class expectedType;

    public JndiObjectLocator() {
    }

    public JndiObjectLocator(Properties environment) {
        this.environment = environment;
    }

    public final Properties getEnvironment() {
        return environment;
    }

    public final void setEnvironment(Properties environment) {
        this.environment = environment;
    }

    public final boolean isResourceRef() {
        return resourceRef;
    }

    public final void setResourceRef(boolean resourceRef) {
        this.resourceRef = resourceRef;
    }

    public final void setJndiName(String jndiName) {
        this.jndiName = jndiName;
    }

    public final void setExpectedType(Class expectedType) {
        this.expectedType = expectedType;
    }

    public Object lookup() throws NamingException {
        String convertedName = convertJndiName();
        Object jndiObject;
        try {
            jndiObject = doLookup(convertedName);
        } catch (NamingException ex) {
            if (!convertedName.equals(jndiName)) {
                // Try fallback to originally specified name...
                jndiObject = doLookup(jndiName);
            } else {
                throw ex;
            }
        }
        return jndiObject;
    }

    private Object doLookup(String jndiName) throws NamingException {
        Context ctx = getContext();
        Object jndiObject;

        try {
            jndiObject = ctx.lookup(jndiName);
            if (jndiObject == null) {
                throw new NameNotFoundException(
                        "JNDI object with [" + jndiName + "] not found: JNDI implementation returned null");
            }
        } finally {
            releaseContext(ctx);
        }

        if (expectedType != null && !expectedType.isInstance(jndiObject)) {
            throw new NamingException("Object of type [" +
                    jndiObject.getClass() +
                    "] available at JNDI location [" +
                    jndiName + "] is not assignable to [" + expectedType.getName() + "]");
        }
        return jndiObject;
    }

    private Context getContext() throws NamingException {
        return createInitialContext();
    }

    private void releaseContext(Context ctx) {
        if (ctx != null) {
            try {
                ctx.close();
            } catch (NamingException ex) {
                //  ignore
            }
        }
    }

    private Context createInitialContext() throws NamingException {
        Hashtable icEnv = null;
        Properties env = getEnvironment();
        if (env != null) {
            icEnv = new Hashtable(env);
        }
        return new InitialContext(icEnv);
    }

    private String convertJndiName() {
        // Prepend container prefix if not already specified and no other scheme given.
        if (isResourceRef() && !jndiName.startsWith(CONTAINER_PREFIX) && jndiName.indexOf(':') == -1) {
            jndiName = CONTAINER_PREFIX + jndiName;
        }
        return jndiName;
    }
}
